package HandshakeJoin;

import java.util.Random;
import java.util.Calendar;
import com.google.common.base.Preconditions;

public class LoadShedder
{
    private int capacity;
    private int thrhStartShedding;
    private double shedFatorBase;
    private double shedFatorDelta;
    private boolean isShedFatorDeltaChanged;
    private Random random;

    LoadShedder(final int capacity, final int thrhStartShedding,
            final double shedFatorBase) {
        Preconditions.checkArgument(thrhStartShedding >= 0);
        Preconditions.checkArgument(capacity > thrhStartShedding);
        Preconditions.checkArgument(shedFatorBase >= 0 && shedFatorBase <= 1);

        this.capacity = capacity;
        this.thrhStartShedding = thrhStartShedding;
        this.shedFatorBase = shedFatorBase;
        shedFatorDelta = calcShedFactorDelta();
        random = new Random(Calendar.getInstance().getTimeInMillis());
    }

    public boolean shedIt(final int loadSize) {
        if (loadSize < thrhStartShedding) {
            return false;
        }
        else if (loadSize >= capacity) {
            return true;
        }
        else {
            if (isShedFatorDeltaChanged) {
                calcShedFactorDelta();
            }

            double shedFator = calcShedFactor(loadSize);
            return (shedFator >= random.nextDouble());
        }
    }

    private double calcShedFactorDelta() {
        double delta = (1 - shedFatorBase) / (capacity - thrhStartShedding);

        isShedFatorDeltaChanged = false;

        return delta;
    }

    private double calcShedFactor(final int loadSize) {
        return (shedFatorBase + shedFatorDelta * (loadSize - thrhStartShedding));
    }

    public LoadShedder setCapacity(final int capacity) {
        this.capacity = capacity;
        isShedFatorDeltaChanged = true;

        return this;
    }

    public LoadShedder setThresholdStartShedding(final int loadSize) {
        this.thrhStartShedding = loadSize;
        isShedFatorDeltaChanged = true;

        return this;
    }

    public LoadShedder setShedFatorBase(final double shedFator) {
        this.shedFatorBase = shedFator;
        isShedFatorDeltaChanged = true;

        return this;
    }
}